package algorithm;

import io.OutputWriter;
import io.SxpcFileReader;
import java.io.IOException;
import java.util.HashMap;
import datastructures.Interaction;
import datastructures.SNPInfo;
import datastructures.VariableList;
import pac.SearchDatastructure;

public class ClusterResults {

	private SearchDatastructure searcher;
	private OutputWriter outputter;
	private Interaction[] entries;
	
	public ClusterResults ( SearchDatastructure I, OutputWriter O ) throws IOException 
	{
		searcher = I;
		outputter = O;
	}

	 
	/*
	 * process hitsfile
	 */
	private void readHitsFile ( String hitsFileName ) {
		try{
			SxpcFileReader s = new SxpcFileReader();
			entries = s.readProcessedHitsFile(hitsFileName);
			if(entries == null) {
				System.out.println("Nothing to process.");
				System.exit(1);
			}			
		}	catch (IOException e) {			
				e.printStackTrace();
				System.exit(1);
		}
	}
	
	/*
	 * Clusters the hits file into one entry per median position.
	 */
	private HashMap<VariableList, HashMap<VariableList, Integer>> clusterInteractions (Interaction[] unclustered) 
	{
		
		HashMap<VariableList, HashMap<VariableList, Integer>> hashtree 
					= new HashMap<VariableList, HashMap<VariableList, Integer>>();
		
		boolean addedLevel1 = false;
		boolean addedLevel2 = false;
		
		double windowSize = searcher.userParams.getBlockSizeBp();
		for(Interaction current : unclustered) 
		{
			long[] current_bp = current.getBpdistance();
			int[] current_chr = current.getChromosome();
			int[] current_snps = current.getMapfileIndex();
			String[] current_modes = current.getMode();
			int[] current_vars = new int[2];
			for(int i=0; i<2; i++) 
			{
				int offset = (current_modes[0].compareTo("d") == 0) ? 0 : 1;
				current_vars[i] = 2*(current_snps[i]-1) + offset;
			}
			
			addedLevel1 = false;
			addedLevel2 = false;
				
			for (VariableList var1 : hashtree.keySet() ) 
			{
				SNPInfo snp1 = searcher.snpList[var1.medianVariable()/2];
				if ( current_chr[0] != snp1.getChromosome() || Math.abs(current_bp[0] - snp1.getBp_distance()) > windowSize )
					continue;
				
				//else				
				HashMap<VariableList, Integer> subtree = hashtree.get(var1);
				var1.addVariable(current_vars[0]);
				
				for ( VariableList var2 : subtree.keySet() ) 
				{
					SNPInfo snp2 = searcher.snpList [ var2.medianVariable()/2 ];
					if ( current_chr[1] != snp2.getChromosome() || Math.abs(current_bp[1] - snp2.getBp_distance()) > windowSize )
						continue;
					
					//else, add to an existing entry
					Integer value = subtree.get(var2) + 1;
					var2.addVariable(current_vars[1]);				
					
					//make a note of level 2 actions
					subtree.put(var2, value);
					addedLevel2 = true;
					break;
				}
				
				//make a new subtree entry, if you didn't find one earlier.
				if( addedLevel2 == false ) 
				{
					VariableList new_level2 = new VariableList(current_vars[1]);
					subtree.put(new_level2, 1);
				}
				
				//make a note of your level 2 actions, whichever they may be, at level 1
				hashtree.put(var1, subtree);
				addedLevel1 = true;
				break;
			
			}
			
			//make an entirely new tree AND subtree entry
			if( addedLevel1 == false ) 
			{
				VariableList new_level1 = new VariableList(current_vars[0]);
				VariableList new_level2 = new VariableList(current_vars[1]);
				HashMap<VariableList, Integer> subtree = new HashMap<VariableList, Integer>();
				subtree.put(new_level2, 1);
				hashtree.put(new_level1, subtree);
			}
			
		}

		return hashtree;
	}
	
	
	public void doClustering ( String hitsFileName ) throws IOException 
	{
		readHitsFile(hitsFileName);
		
		HashMap<VariableList, HashMap<VariableList, Integer>> hashtree = clusterInteractions(entries);

		for ( VariableList var1 : hashtree.keySet() ) 
		{
			HashMap<VariableList, Integer> subtree = hashtree.get( var1 );
			
			for ( VariableList var2 : subtree.keySet() ) 
			{ 
				
				Integer count = subtree.get ( var2 );
				int[] variableArray = new int[] {var1.medianVariable(), var2.medianVariable()};
				
				//2.5 : establish expected and observed on number of case carriers
				int[][] noOfCaseCarriers = searcher.snpIndices.getObservedCaseCarriers(searcher.userParams, variableArray);		
				double pvalueCaseOnly = searcher.significance.TestCasesOnly ( 2, noOfCaseCarriers );

				int[][] noOfControlCarriers = searcher.snpIndices.getObservedControlCarriers(searcher.userParams, variableArray);
				double pvalueCaseControl = searcher.significance.TestCasesVsControls ( 2, noOfCaseCarriers, noOfControlCarriers );
				
				try {
					outputter.WriteOutputClustered( variableArray, 
																				noOfCaseCarriers, pvalueCaseOnly, 
																				noOfControlCarriers, pvalueCaseControl, 
																				count ) ;
				} catch (IOException e) {
					e.printStackTrace();
				}
					
			}	//end snp2 for loop
		}	//end snp1 for loop
		
		
		System.out.println("Finished.");
		outputter.Close();
	
	}
	
	
}
